Ego Network | Social Network Analysis¶

Dr. Chan, Chun-Hsiang @ Department of Geography,
National Taiwan Normal University, Taipei, Taiwan

In [1]:
# import packages
import numpy as np
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
In [2]:
G = nx.erdos_renyi_graph(50, 0.5, seed=21, directed=False)

options = {
    "font_size": 8,
    "node_size": 100,
    "node_color": "#A0CBE2",
    "edge_color": "brown",
    "linewidths": 0.1,
    "width": 0.08,
}

plt.subplots(figsize=[12,6], dpi=100)
nx.draw_networkx(G,  **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [3]:
# is the graph connected?
nx.is_connected(G)
Out[3]:
True
In [4]:
# number of connected component
nx.number_connected_components(G)
Out[4]:
1
In [5]:
# the set of nodes in the component of graph containing node n.
np.array(nx.node_connected_component(G, 11))
Out[5]:
array({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49},
      dtype=object)
In [6]:
# network initialization

DG = nx.fast_gnp_random_graph(100, 0.005, seed=21, directed=True)

options = {
    "font_size": 8,
    "node_size": 100,
    "node_color": "#A0CBE2",
    "edge_color": "brown",
    "linewidths": 0.1,
    "width": 0.08,
}

plt.subplots(figsize=[12,6], dpi=100)
nx.draw_networkx(DG,  **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [7]:
# network initialization
DG1 = nx.gnm_random_graph(20, 40, seed=10, directed=True)

options = {
    "font_size": 8,
    "node_size": 100,
    "node_color": "#A0CBE2",
    "edge_color": "brown",
    "linewidths": 0.1,
    "width": 0.08,
}

plt.subplots(figsize=[12,6], dpi=100)
nx.draw_networkx(DG1,  **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()
In [8]:
# network initialization
DG2 = nx.gnm_random_graph(20, 100, seed=10, directed=True)

options = {
    "font_size": 8,
    "node_size": 100,
    "node_color": "#A0CBE2",
    "edge_color": "brown",
    "linewidths": 0.1,
    "width": 0.08,
}

plt.subplots(figsize=[12,6], dpi=100)
nx.draw_networkx(DG2,  **options, with_labels = True)
ax = plt.gca()
ax.margins(0.001)
plt.axis("off")
plt.show()

Weak, Regular, and Strong connectivity in directed graphs¶

1) weakly connected - replacing all of G's directed edges with undirected edges produces a connected (undirected) graph.
2) connected - contains a directed path from u to v OR a directed path from v to u for every pair of vertices u, v
3) strongly connected - contains a directed path from u to v AND a directed path from v to u for every pair of vertices u, v
Source: https://math.stackexchange.com/questions/1614641/weak-regular-and-strong-connectivity-in-directed-graphs

In [9]:
# Test directed graph for strong connectivity
nx.is_strongly_connected(DG)
Out[9]:
False
In [10]:
# number of strongly connected components in graph
nx.number_strongly_connected_components(DG)
Out[10]:
100
In [11]:
# Test directed graph for weak connectivity
nx.is_weakly_connected(DG)
Out[11]:
False
In [12]:
# the number of weakly connected components in G
nx.number_weakly_connected_components(DG)
Out[12]:
50

Lab Practice:
Please try three random graphs (DG, DG1, and DG2) with and give your observation with explanation.

In [13]:
# your answer:

Network-level structure¶

In [14]:
# network density
nx.density(DG), nx.density(DG1), nx.density(DG2)
Out[14]:
(0.005050505050505051, 0.10526315789473684, 0.2631578947368421)
In [15]:
# number of edges in the graph
nx.number_of_edges(DG)
Out[15]:
50
In [16]:
# number of nodes in the graph
nx.number_of_nodes(DG)
Out[16]:
100
In [17]:
# number of isolates
nx.number_of_isolates(DG)
Out[17]:
36
In [18]:
# number of self loop
nx.number_of_selfloops(DG)
Out[18]:
0
In [19]:
# average of shortest path length in the network
nx.average_shortest_path_length(DG2)
Out[19]:
1.9631578947368422
In [20]:
# diameter of the network
nx.diameter(DG2)
Out[20]:
4

Structure hole¶

In [21]:
# graph initialization
neck = nx.Graph([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (4, 5), (3, 5)])
attr_5 = {0: "A", 1: "B", 2: "C", 3: "D", 4: "E", 5: "F"}
neck = nx.relabel_nodes(neck, attr_5)

options = {
    "font_size": 14,
    "node_size": 800,
    "node_color": "#A0CBE2",
    "edge_color": "skyblue",
    "linewidths": 1,
    "width": 2,
}

# draw the graph
plt.subplots(figsize=[6,6], dpi=100)
nx.draw_networkx(neck,  **options, with_labels = True)
ax = plt.gca()
ax.margins(0.20)
plt.axis("off")
plt.show()
In [22]:
# structure hole: effective size
nx.algorithms.structuralholes.effective_size(neck)
Out[22]:
{'A': 1.0,
 'B': 1.0,
 'C': 2.3333333333333335,
 'D': 2.3333333333333335,
 'E': 1.0,
 'F': 1.0}
In [23]:
# structure hole: constraint
A_neck = nx.adjacency_matrix(neck)
A_neck = np.array(A_neck.todense())
A_neck
Out[23]:
array([[0, 1, 1, 0, 0, 0],
       [1, 0, 1, 0, 0, 0],
       [1, 1, 0, 1, 0, 0],
       [0, 0, 1, 0, 1, 1],
       [0, 0, 0, 1, 0, 1],
       [0, 0, 0, 1, 1, 0]], dtype=int32)
In [24]:
# structure hole: constraint
rowS_neck = np.sum(A_neck, axis=1)
rowS_neck = np.transpose([rowS_neck])
rowS_neck
Out[24]:
array([[2],
       [2],
       [3],
       [3],
       [2],
       [2]], dtype=int32)
In [25]:
# structure hole: constraint
p = A_neck/rowS_neck
p
Out[25]:
array([[0.        , 0.5       , 0.5       , 0.        , 0.        ,
        0.        ],
       [0.5       , 0.        , 0.5       , 0.        , 0.        ,
        0.        ],
       [0.33333333, 0.33333333, 0.        , 0.33333333, 0.        ,
        0.        ],
       [0.        , 0.        , 0.33333333, 0.        , 0.33333333,
        0.33333333],
       [0.        , 0.        , 0.        , 0.5       , 0.        ,
        0.5       ],
       [0.        , 0.        , 0.        , 0.5       , 0.5       ,
        0.        ]])
In [50]:
# structure hole: constraint
p*p
Out[50]:
array([[0.        , 0.25      , 0.25      , 0.        , 0.        ,
        0.        ],
       [0.25      , 0.        , 0.25      , 0.        , 0.        ,
        0.        ],
       [0.11111111, 0.11111111, 0.        , 0.11111111, 0.        ,
        0.        ],
       [0.        , 0.        , 0.11111111, 0.        , 0.11111111,
        0.11111111],
       [0.        , 0.        , 0.        , 0.25      , 0.        ,
        0.25      ],
       [0.        , 0.        , 0.        , 0.25      , 0.25      ,
        0.        ]])
In [56]:
# structure hole: constraint
cij = (p+p*p)**2
cij
Out[56]:
array([[0.        , 0.5625    , 0.5625    , 0.        , 0.        ,
        0.        ],
       [0.5625    , 0.        , 0.5625    , 0.        , 0.        ,
        0.        ],
       [0.19753086, 0.19753086, 0.        , 0.19753086, 0.        ,
        0.        ],
       [0.        , 0.        , 0.19753086, 0.        , 0.19753086,
        0.19753086],
       [0.        , 0.        , 0.        , 0.5625    , 0.        ,
        0.5625    ],
       [0.        , 0.        , 0.        , 0.5625    , 0.5625    ,
        0.        ]])
In [57]:
# structure hole: constraint
np.sum(cij, axis=1)
Out[57]:
array([1.125     , 1.125     , 0.59259259, 0.59259259, 1.125     ,
       1.125     ])
In [53]:
# structure hole: constraint
# nx.constraint(neck)
nx.algorithms.structuralholes.constraint(neck)
Out[53]:
{'A': 1.0069444444444444,
 'B': 1.0069444444444444,
 'C': 0.6111111111111112,
 'D': 0.6111111111111112,
 'E': 1.0069444444444444,
 'F': 1.0069444444444444}
In [41]:
nx.algorithms.local_constraint(neck, 'A','B'), nx.algorithms.local_constraint(neck, 'A','C')
Out[41]:
(0.4444444444444444, 0.5625)
In [42]:
nx.algorithms.local_constraint(neck, 'A','D'), nx.algorithms.local_constraint(neck, 'A','E')
Out[42]:
(0.027777777777777776, 0.0)
In [43]:
nx.algorithms.local_constraint(neck, 'A','F')
Out[43]:
0.0
In [45]:
0.4444444444444444+0.5625+0.027777777777777776
Out[45]:
1.034722222222222
In [59]:
# graph initialization
neck = nx.Graph([(1, 2), (1, 3), (1, 4), (1, 5), (4, 5), (2, 5)])
# attr_5 = {0: "A", 1: "B", 2: "C", 3: "D", 4: "E", 5: "F"}
# neck = nx.relabel_nodes(neck, attr_5)

options = {
    "font_size": 14,
    "node_size": 800,
    "node_color": "#A0CBE2",
    "edge_color": "skyblue",
    "linewidths": 1,
    "width": 2,
}

# draw the graph
plt.subplots(figsize=[6,6], dpi=100)
nx.draw_networkx(neck,  **options, with_labels = True)
ax = plt.gca()
ax.margins(0.20)
plt.axis("off")
plt.show()
In [60]:
# structure hole: constraint
A_neck = nx.adjacency_matrix(neck)
A_neck = np.array(A_neck.todense())
A_neck
Out[60]:
array([[0, 1, 1, 1, 1],
       [1, 0, 0, 0, 1],
       [1, 0, 0, 0, 0],
       [1, 0, 0, 0, 1],
       [1, 1, 0, 1, 0]], dtype=int32)
In [61]:
# structure hole: constraint
rowS_neck = np.sum(A_neck, axis=1)
rowS_neck = np.transpose([rowS_neck])
rowS_neck
Out[61]:
array([[4],
       [2],
       [1],
       [2],
       [3]], dtype=int32)
In [62]:
# structure hole: constraint
p = A_neck/rowS_neck
p
Out[62]:
array([[0.        , 0.25      , 0.25      , 0.25      , 0.25      ],
       [0.5       , 0.        , 0.        , 0.        , 0.5       ],
       [1.        , 0.        , 0.        , 0.        , 0.        ],
       [0.5       , 0.        , 0.        , 0.        , 0.5       ],
       [0.33333333, 0.33333333, 0.        , 0.33333333, 0.        ]])
In [76]:
np.matmul(p,p)
Out[76]:
array([[0.58333333, 0.08333333, 0.        , 0.08333333, 0.25      ],
       [0.16666667, 0.29166667, 0.125     , 0.29166667, 0.125     ],
       [0.        , 0.25      , 0.25      , 0.25      , 0.25      ],
       [0.16666667, 0.29166667, 0.125     , 0.29166667, 0.125     ],
       [0.33333333, 0.08333333, 0.08333333, 0.08333333, 0.41666667]])
In [72]:
p*p
Out[72]:
array([[0.        , 0.0625    , 0.0625    , 0.0625    , 0.0625    ],
       [0.25      , 0.        , 0.        , 0.        , 0.25      ],
       [1.        , 0.        , 0.        , 0.        , 0.        ],
       [0.25      , 0.        , 0.        , 0.        , 0.25      ],
       [0.11111111, 0.11111111, 0.        , 0.11111111, 0.        ]])
In [64]:
# structure hole: constraint
cij = (p+p*p.T)**2
cij
Out[64]:
array([[0.        , 0.140625  , 0.25      , 0.140625  , 0.11111111],
       [0.390625  , 0.        , 0.        , 0.        , 0.44444444],
       [1.5625    , 0.        , 0.        , 0.        , 0.        ],
       [0.390625  , 0.        , 0.        , 0.        , 0.44444444],
       [0.17361111, 0.25      , 0.        , 0.25      , 0.        ]])
In [69]:
# nx.constraint(neck)
nx.algorithms.structuralholes.constraint(neck)
Out[69]:
{1: 0.5347222222222222,
 2: 0.8350694444444444,
 3: 1.0,
 4: 0.8350694444444444,
 5: 0.7916666666666665}
In [ ]:
 
In [ ]: